package me.fanchaoo.web.service.impl;

import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import me.fanchaoo.constant.JwtConstant;
import me.fanchaoo.constant.RedisPrefixConst;
import me.fanchaoo.web.dto.InitWXJSSDKConfigDTO;
import me.fanchaoo.web.request.InitWXJSSDKRequest;
import me.fanchaoo.web.response.BaseResponse;
import me.fanchaoo.web.service.WeiXinService;
import me.fanchaoo.web.util.RedisUtils;
import me.fanchaoo.web.util.wx.WXConfig;
import me.fanchaoo.web.util.wx.WXContants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.security.MessageDigest;
import java.util.*;


@Slf4j
@Service
public class WeiXinServiceImpl implements WeiXinService {
    @Autowired
    private WXConfig weiXinConfig;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private RedisUtils redisUtils;
    @Override
    public BaseResponse<InitWXJSSDKConfigDTO> initJSSDKConfigInfo(InitWXJSSDKRequest request)  throws Exception{
        BaseResponse<InitWXJSSDKConfigDTO>  response = new BaseResponse<>();
        InitWXJSSDKConfigDTO  initWXJSSDKConfigDTO = new InitWXJSSDKConfigDTO();
        String appID=weiXinConfig.getAppID();
        String accessToken;
        if (Objects.isNull(redisUtils.getString(RedisPrefixConst.WX_ACCESSTOKEN_KEY))){
            accessToken = this.getJSSDKAccessToken();
            redisUtils.set(RedisPrefixConst.WX_ACCESSTOKEN_KEY, accessToken,60 * 60 * 1);
        }else {
            accessToken = redisUtils.getString(RedisPrefixConst.WX_ACCESSTOKEN_KEY);
        }
        String jsapiTicket = this.getJSSDKJsapiTicket(accessToken);
        String timestamp = Long.toString(System.currentTimeMillis() / 1000);
        String nonceStr = UUID.randomUUID().toString();
        String signature = this.buildJSSDKSignature(jsapiTicket,timestamp,nonceStr,request.getShareUrl());
        initWXJSSDKConfigDTO.setShareUrl(request.getShareUrl());
        initWXJSSDKConfigDTO.setJsapiTicket(jsapiTicket);
        initWXJSSDKConfigDTO.setNonceStr(nonceStr);
        initWXJSSDKConfigDTO.setTimestamp(timestamp);
        initWXJSSDKConfigDTO.setSignature(signature);
        initWXJSSDKConfigDTO.setAppid(appID);
        response.setBody(initWXJSSDKConfigDTO);
        return response;
    }

    /**
     * @description 获取JSSDK的认证token
     * @return 返回JSSDK的认证token
     */
    public String getJSSDKAccessToken() {
        String token = null;
        String url = WXContants.JSSDK_ACCESSTOKEN.replaceAll(WXContants.REPLACE_FEILD_APPID,
                weiXinConfig.getAppID()).replaceAll(WXContants.REPLACE_FEILD_APPSECRET,
                weiXinConfig.getAppsecret());
        String json = postRequestForWeiXinService(url);
        Map map = jsonToMap(json);
        if (map != null) {
            token = (String) map.get("access_token");
        }
        return token;
    }

    /**
     * @description 获取JSSDK的ticket
     * @param token JSSDK的认证token
     * @return 返回JSSDK的ticket
     */
    public String getJSSDKJsapiTicket(String token) {
        String url = WXContants.JSSDK_TICKET.replaceAll(WXContants.REPLACE_FEILD_ACCESS_TOKEN, token);
        String json = postRequestForWeiXinService(url);
        Map map = jsonToMap(json);
        String ticket = null;
        if (map != null) {
            ticket = (String) map.get("ticket");
        }
        return ticket;
    }

    /**
     * @description 构建分享链接的签名
     * @param ticket
     * @param nonceStr
     * @param timestamp
     * @param url
     * @return
     * @throws Exception
     */
    public static String buildJSSDKSignature(String ticket,String timestamp,String nonceStr ,String url) throws Exception {
        StringBuffer signaStr = new StringBuffer();
        signaStr.append("jsapi_ticket=").append(ticket);
        signaStr.append("&noncestr=").append(nonceStr);
        signaStr.append("&timestamp=").append(timestamp);
        signaStr.append("&url=").append(url);
        return sha1(signaStr.toString());
    }


    /**
     * @description sha1 加密JSSDK微信配置参数获取签名
     * @param signaStr 构建分享链接的签名
     * @return 返回加密签名
     */
    public static String sha1(String signaStr) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        byte[] digest = md.digest(signaStr.getBytes());
        return byteToStr(digest).toLowerCase();
    }
    /**
     * @description 将字节数组转换为十六进制字符串
     * @param byteArray 待转换的字节数组
     * @return
     */
    private static String byteToStr(byte[] byteArray) {
        String strDigest = "";
        for (int i = 0; i < byteArray.length; i++) {
            strDigest += byteToHexStr(byteArray[i]);
        }
        return strDigest;
    }

    /**
     * @description  将字节转换为十六进制字符串
     * @param mByte
     * @return
     */
    private static String byteToHexStr(byte mByte) {
        char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        char[] tempArr = new char[2];
        tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
        tempArr[1] = Digit[mByte & 0X0F];
        String s = new String(tempArr);
        return s;
    }

    /**
     * @description  将map转换成json字符串
     * @param map map对象
     * @return json字符串
     */
    public String mapToJson(Map map){
        Gson gson = new Gson();
        String json = gson.toJson(map);
        return json;
    }

    /**
     * @description  将json字符串转换成map字符串
     * @param json json字符串
     * @return map对象
     */
    private Map jsonToMap(String json) {
        Gson gons = new Gson();
        Map map = gons.fromJson(json, new TypeToken<Map>(){}.getType());
        return map;
    }
    private String postRequestForWeiXinService(String accessTokenUrl) {
        ResponseEntity<String> postForEntity = restTemplate.postForEntity(accessTokenUrl, null, String.class);
        String json = postForEntity.getBody();
        return json;
    }

    private String getRequestForWeiXinService(String getUserInfoUrl) {
        ResponseEntity<String> postForEntity = restTemplate.getForEntity(getUserInfoUrl.toString(), String.class);
        String json = postForEntity.getBody();
        return json;
    }
}
